package main

import (
	"fmt"
	"testing"
)

//Operator 是被封装的实际类接口
type Operator interface {
	SetA(int)
	SetB(int)
	Result() int
}

// OperatorFactory 是工厂接口
type OperatorFactory interface {
	Create() Operator
}

// OperatorBase 是Operator 接口实现的基类，封装公用方法
type OperatorBase struct {
	a, b int
}

// SetA 设置 A
func (o *OperatorBase) SetA(a int) {
	o.a = a
}

// SetB 设置 B
func (o *OperatorBase) SetB(b int) {
	o.b = b
}

// PlusOperatorFactory 是 PlusOperator 的工厂类
type PlusOperatorFactory struct{}

func (PlusOperatorFactory) Create() Operator {
	return &PlusOperator{
		OperatorBase: &OperatorBase{},
	}
}

//PlusOperator Operator 的实际加法实现
type PlusOperator struct {
	*OperatorBase
}

//Result 获取结果
func (o PlusOperator) Result() int {
	return o.a + o.b
}

//MinusOperatorFactory 是 MinusOperator 的工厂类
type MinusOperatorFactory struct{}

func (MinusOperatorFactory) Create() Operator {
	return &MinusOperator{
		OperatorBase: &OperatorBase{},
	}
}

//MinusOperator Operator 的实际减法实现
type MinusOperator struct {
	*OperatorBase
}

//Result 获取结果
func (o MinusOperator) Result() int {
	return o.a - o.b
}

func compute(factory OperatorFactory, a, b int) int {
	op := factory.Create()
	op.SetA(a)
	op.SetB(b)
	return op.Result()
}

func TestOperator(t *testing.T) {

	/*
		PlusOperatorFactory加法工厂创建一个PlusOperator(抽象类为Operator)的对象，PlusOperator对象组合了父级有setA和setB，自身有Result加法计算结果，compute执行对应实现的方法
	*/
	if compute(PlusOperatorFactory{}, 1, 2) != 3 {
		fmt.Println("error with factory method pattern")
	} else {
		fmt.Println("加法 true ")
	}

	/*
		MinusOperatorFactory减法工厂创建一个MinusOperator(抽象类为Operator)的对象，MinusOperator对象组合了父级有setA和setB，自身有Result减法计算结果，compute执行对应实现的方法
	*/
	if compute(MinusOperatorFactory{}, 4, 2) != 2 {
		fmt.Println("error with factory method pattern")
	} else {
		fmt.Println("减法 true ")
	}

}

func main() {
	TestOperator(&testing.T{})
}

/*

工厂方法模式 是对简单工厂模式的进一步抽象化
优点：
	用户只需要知道具体工厂的名称就可得到所要的产品，无须知道产品的具体创建过程。
	灵活性增强，对于新产品的创建，只需多写一个相应的工厂类。
	典型的解耦框架。高层模块只需要知道产品的抽象类，无须关心其他实现类，满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点：
	类的个数容易过多，增加复杂度
	增加了系统的抽象性和理解难度
	抽象产品只能生产一种产品，此弊端可使用抽象工厂模式解决。

应用场景：
	客户只知道创建产品的工厂名，而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
	创建对象的任务由多个具体子工厂中的某一个完成，而抽象工厂只提供创建产品的接口。
	客户不关心创建产品的细节，只关心产品的品牌


# 工厂方法模式

工厂方法模式使用子类的方式延迟生成对象到子类中实现。

Go中不存在继承 所以使用匿名组合来实现
*/
